#include "QbBondModule.h"

#include <bonddict/IBondDict.h>
#include <bondlib/BondContainer.h>
#include <bondlib/QBCategoryFlagInfo.h>
#include <include/CmdDefines.h>
#include <qbkernel/include/qb_base.h>
#include <qbtools/message/RequestNotifier.h>
#include <uam/ServerTimeMgr.h>
#include <uam/UserAccountManager.h>

#include <array>
#include <future>

#include "BestQuote/BestQuoteDlg.h"
#include "BondDataCenter.h"
#include "BondSearch.h"
#include "Detail/BondDetailDlg.h"
#include "HomePage.h"
#include "QbBondDefines.h"
#include "QbMessageProcess.h"
#include "Web/QbCefQueryHandler.h"
#include "bondlib/Pinyin.h"
#include "widgets/cefview/CefViewWidget.h"

using namespace qb;

extern "C" S_QBBOND_EXPORT IModule* getModule() {
  return &QbBondModule::instance();
}

QbBondModule& QbBondModule::instance() {
  static QbBondModule s_module;
  return s_module;
}

void QbBondModule::initialize(IApplication* app) {
  m_app = app;
  m_app->mainFrame()->searchWidget()->setSearchEngine(new MainSearchEngine());

  CBondContainer::instance().CalcRemainingDate(
      ServerTimeMgr::instance()
          .serverTime());  // TODO:是不是应该放在登录时收到servertime的时候?
  CBondContainer::instance().CalcBondExpireInHoliday();

  int nSize = CBondContainer::instance().GetSize();
  CQBCategoryFlagInfo& cfInfo = CQBCategoryFlagInfo::instance();
  QBCATEGORYFLAGINFO qCfInfo;
  for (int i = 0; i < nSize; i++)  // BIGLOOP
  {
    const CBondInfo& info = CBondContainer::instance().ElementAtR(i);
    std::string cf = info.GetCategoryFlag();
    if (!cf.empty()) {
      FIELDCOPY(qCfInfo.CF_Name, cf.c_str());
      cfInfo.Add(qCfInfo);
    }
  }
  cfInfo.LoadDataAndAlphabetSort();

  registNetEvent();
  qbbond::InitDecodeFuncs();
  // TODO: 需要在线程中运行，并且等待权限获取完成
  requestBondData();

  std::thread t([]() { getDictUpdate()->WaitFirstLoad(0, 100); });
  t.detach();

  m_pCefViewDelegate.reset(new QbCefViewDelegate());
}

void QbBondModule::unInitialize() {
  m_pCefViewDelegate.reset(nullptr);

  unRegistNetEvent();
  unRegistSpecificMsgProcessor();
}

bool QbBondModule::showWebPage(const QString& url) {
  const int cmdId = -1;  // special for web?

  IMainFrame* frame = m_app->mainFrame();
  const int tabId = frame->existCmdWindow(cmdId, url, true);
  if (tabId != -1) {
    frame->activateCmdWindow(tabId);
    return false;
  }

  CefViewWidget* cefview_widget = new CefViewWidget(url, nullptr, nullptr);
  QObject::connect(cefview_widget, &CefViewWidget::cefKeyEvent,
                   [this](const QKeyEvent& event) {
                     if (auto p_app = app())
                       if (auto p_mainframe = p_app->mainFrame())
                         if (auto p_search = p_mainframe->searchWidget()) {
                           log_info("onBrowserKeyEvent: key=%d, char=%s",
                                    event.key(),
                                    event.text().toStdString().c_str());
                           QKeyEvent* _event = new QKeyEvent(event);
                           QApplication::postEvent(p_search, _event);
                         }
                   });

  cefview_widget->setDelegate(m_pCefViewDelegate);
  const int winId = frame->openCmdWindow(cmdId, url, cefview_widget, "");
  QObject::connect(cefview_widget, &CefViewWidget::titleChanged,
                   [winId, this](const QString& title) {
                     if (auto p_app = app())
                       if (auto p_mainframe = p_app->mainFrame())
                         p_mainframe->updateWindowTitle(winId, title);
                   });

  return true;
}

int QbBondModule::execCommand(CmdInfo ci) const {
  IMainFrame* frame = m_app->mainFrame();
  QString paraStr = QString::fromUtf8(ci.parameters);
  QString titleStr = QString::fromUtf8(ci.title);

  if (CMD_WebBondDetail_Profile == ci.cmdId) {
    // paraStr: "${bondkey}.${listmarket}|${subpage}"
    QStringList paras = paraStr.split("|");
    const QString bondKey =
        (paras.size() > 0) ? paras[0] : "G0001242021FINPBB05.CIB";

    const int bondIndex =
        CBondContainer::instance().GetBondIndex(bondKey.toStdString());
    if (bondIndex < 0) return 1101;

    const CBondInfo& bi = CBondContainer::instance().ElementAtR(bondIndex);
    if (!bi.IsValidBond()) return 1102;

    const std::string subPage =
        (paras.size() > 1) ? paras[1].toStdString() : "PROFILE";

    QString url = SSUserAccountManager::instance().GetWebOption().LoadURL(
        std::string("WEB_BONDDETAIL_").append(subPage).c_str());
    url = url.asprintf(url.toUtf8(), bi.GetBondKey(), bi.GetListedMarket());
    titleStr = QObject::tr("债券深度详情");

    // we call it: expand/explain "paraStr"?
    paraStr = url;
  }

  // 模块可以自己决定是否多开
  int tabId = frame->existCmdWindow(ci.cmdId, paraStr, ci.raw_param);

  if (tabId != -1) {
    if (77000 == ci.actualCmdId) {
      QWidget* widget = frame->cmdWindow(tabId);
      if (widget) {
        CefViewWidget* cefview_widget = dynamic_cast<CefViewWidget*>(widget);
        if (cefview_widget) {
          static const std::array<int, 2> need_reload = {CMD_KFB,
                                                         CMD_KungfuNewIssue};
          // static const std::array<int, 2> need_navigate =
          // {CMD_IssuerInformation, CMD_WebBondDetail_Profile};
          if (need_reload.end() !=
              std::find(need_reload.begin(), need_reload.end(), ci.cmdId)) {
            cefview_widget->browserReload();
          } else if (!ci.raw_param) {  //(need_navigate.end() !=
                                       //std::find(need_navigate.begin(),
                                       //need_navigate.end(), ci.cmdId)) {
            cefview_widget->navigateToUrl(paraStr);  // "%%user&pwd" not handled
          }
        }
      }
    }

    frame->activateCmdWindow(tabId);
  } else {
    QWidget* widget = nullptr;
    int actualCmdId = ci.actualCmdId;
    if (CMD_BondDetail == actualCmdId) {
      if (CBondInfo* bi = CBondContainer::instance().GetBond(ci.parameters)) {
        widget = new BondDetailDlg(nullptr);
        static_cast<BondDetailDlg*>(widget)->setCombondKey(ci.parameters);
        titleStr = QString::fromLocal8Bit(bi->GetBondShortName());
      } else {
        return 1103;
      }
    } else if (CMD_BrokerBestPrice == ci.cmdId) {
      widget = new BestQuoteDlg(nullptr);
    }
#if 0
        else if (CMD_WebBondDetail_Profile == ci.cmdId)
		{
			QStringList paras = paraStr.split("|");
			QString bondKey = "G0001242021FINPBB05.CIB";
			if (paras.size() > 0)
				bondKey = paras[0];
			int bondIndex = CBondContainer::instance().GetBondIndex(bondKey.toStdString());
			if (bondIndex < 0)
				return 0;

			const CBondInfo& bi = CBondContainer::instance().ElementAtR(bondIndex);
			if (!bi.IsValidBond())
				return 0;

			std::string subPage = "PROFILE";
			if (paras.size() > 1)
			{
				subPage = paras[1].toStdString();
			}
			QString url = SSUserAccountManager::instance().GetWebOption().LoadURL(std::string("WEB_BONDDETAIL_").append(subPage).c_str());
			url = url.asprintf(url.toUtf8(), bi.GetBondKey(), bi.GetListedMarket());
			titleStr = QObject::tr("债券深度详情");
			paraStr = bondKey;

            tabId = frame->existCmdWindow(ci.cmdId, paraStr);
            if(tabId == -1) {
                // Copied code -- need to be reconstruct later.
                CefViewWidget *cefview_widget = new CefViewWidget(url, nullptr, nullptr);
                QObject::connect(cefview_widget, &CefViewWidget::cefKeyEvent, [this](const QKeyEvent& event){
                    if (auto p_app = app())
                        if (auto p_mainframe = p_app->mainFrame())
                            if (auto p_search = p_mainframe->searchWidget()) {
                                QKeyEvent *_event = new QKeyEvent( event );
                                QApplication::postEvent(p_search, _event);
                            }
                });

                cefview_widget->setDelegate(m_pCefViewDelegate);
                widget = cefview_widget;
            } else {
				widget = frame->cmdWindow(tabId);
                CefViewWidget *cefview_widget = dynamic_cast<CefViewWidget *>(widget);
                if (cefview_widget) {
                    cefview_widget->navigateToUrl(url);
                }
			}
		}
#endif
    else if (77000 == actualCmdId) {
      QString sUser = QString::fromLatin1(
          SSUserAccountManager::instance().GetUserAccount());
      QString sPwd = QString::fromLatin1(
          SSUserAccountManager::instance().GetUserPassword());
      QString sUserPwd =
          QString("username=").append(sUser).append("&pwd=").append(sPwd);
      QString url = paraStr;
      url.replace("%%user&pwd", sUserPwd.toUtf8());

      CefViewWidget* cefview_widget = new CefViewWidget(url, nullptr, nullptr);
      QObject::connect(cefview_widget, &CefViewWidget::cefKeyEvent,
                       [this](const QKeyEvent& event) {
                         if (auto p_app = app())
                           if (auto p_mainframe = p_app->mainFrame())
                             if (auto p_search = p_mainframe->searchWidget()) {
                               log_info("onBrowserKeyEvent: key=%d, char=%s",
                                        event.key(),
                                        event.text().toStdString().c_str());
                               QKeyEvent* _event = new QKeyEvent(event);
                               QApplication::postEvent(p_search, _event);
                             }
                       });

      if (ci.cmdId == CMD_NGPC) {
        cefview_widget->setDelegate(
            QSharedPointer<QbCefViewDelegate>(new QbCefViewDelegate()));
      } else {
        cefview_widget->setDelegate(m_pCefViewDelegate);
      }

      widget = cefview_widget;
    } else if (20044 == actualCmdId) {
      widget = new HomePage();
    }

    if (widget) frame->openCmdWindow(ci.cmdId, paraStr, widget, titleStr);
  }
  return 0;
}

bool QbBondModule::closeCommandWindow(int cmdId, QWidget* widget) {
  if (!widget) return true;

  widget->setParent(nullptr);
  delete widget;

  return true;
}

void QbBondModule::requestBondData() {
  BondDataCenter::instance().requestQBACDCPriceNonSpecial();
  BondDataCenter::instance().requestQBACDCPriceSpecial();
  BondDataCenter::instance().requestBestPrice();
  BondDataCenter::instance().requestBrokerPriceReg();
  BondDataCenter::instance().requestQBMarketStreamDeal();
  BondDataCenter::instance().requestAllCSIV2();
  BondDataCenter::instance().requestBondLiquidity();
  BondDataCenter::instance().requestIssuerLiquidity();
  BondDataCenter::instance().requestHighYieldBond();
  BondDataCenter::instance().requestThirdPartyGuarantee();
  BondDataCenter::instance().requestSectorBond();
  BondDataCenter::instance().requestMarketMakeTarget();
}

void QbBondModule::registNetEvent() {
  qbBase::getNetworkService()->RegisterNetworkEventObserver(this);
}

void QbBondModule::unRegistNetEvent() {
  qbBase::getNetworkService()->DeRegisterNetworkEventObserver(this);
}

STDMETHODIMP_(void) QbBondModule::OnRecover(THIS_ int32_t connect_id) {
  requestBondData();
}
